/*	
 * Authors: Ken YANG <spng.yang@gmail.com>
 * 	
 * Mammoth main program
 *
 * Copyright (C) 2007-2008 Ken YANG
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation, version 2. 
 * THIS SOFTWARE COMES "AS IS" WITHOUT ANY WARRANTIES.
 */

#include "mmh.h"


extern char *optarg;
extern int optind;

/* options given on commond line */
int mmh_verbose;
char *mmh_host;		/* remote host */
char *mmh_service;	/* service name or port number */
int mmh_client=1;	/* Flags for indicating client(default) */
int mmh_server=0;	/* Flags for indicating server */
size_t mmh_connections;	/* number of connections in TCP_MULTCONN */
int mmh_bindport;	/* tcp or udp port number to bind */
char mmh_localaddr[32];	/* local address.port, dotted-decimal */
int mmh_udp_f=0;	/* flags for identifying UDP protocol */
int mmh_wsize=1024;	/* Bytes per write, 1024 default */
int mmh_rsize=1024;	/* Bytes per read, 1024 default */
char *mmh_rbuf=NULL;	/* pointer to user-level read buffer */
char *mmh_wbuf=NULL;	/* pointer to user-level write buffer */
int mmh_nbuf=1024; 	/* Number of buffers to write */
int mmh_rcvbuf_size=0;	/* Size for SO_RCVBUF */
int mmh_sndbuf_size=0;	/* Size for SO_SNDBUF */
int mmh_tcpmss=0;	/* TCP_MAXSEG */
int mmh_broadcast=0;	/* if set SO_BROADCAST */
int mmh_keepalive=0;	/* Flags for SO_KEEPALIVE */
char mmh_foreign[128];	/* Foreign ip address, Max(128) is IPv6 */
int mmh_foreignport;	/* Port in foreign addr */
int mmh_listen_queue=5;	/* Listen queue for TCP server */
int mmh_concurrent=0;	/* Concurrent server, iterative default */
int mmh_udpconnect=1;	/* Connect even for udp */
/* 
 * number of threads in threads poll to process request, create 
 * 16 threads default
 * */
int mmh_nthreads=16;
int mmh_listenfd;	/* server listening socket */
/* end - options */

/* thread relative structure */
struct thread_poll_t{
	pthread_t tid;	/* thread ID */
	long conn_handle;	/* connection handled by thread */
	/* left empty for futhur use */
};
struct thread_poll_t *tpoll;	/* thread poll, calloc'ed */
/* mutex lock for accept() */
pthread_mutex_t mmh_accept_lock=PTHREAD_MUTEX_INITIALIZER;
/* end - thread */

/* 
 * Although these two name is same with mammoth server, 
 * but it's ok, because they are not in same namespace.
 * */
struct sockaddr_in mmh_cliaddr,mmh_servaddr;
socklen_t mmh_addrlen;

enum emu_modes {TCP_BULK, TCP_MULTCONN};
struct mmh_emu_t{
	enum emu_modes mode;
	/* left empty for futhur use */
};
/*static struct mmh_emu_type *emu_types=NULL;*/
static struct mmh_emu_t mmh_emu_class;
static int num_types=0;
/* end - option struct */

/*
 * Name: cleanup()
 *
 * Description: 
 * Firstly, there is nothing but "emu_types" free. But, in 
 * future, if some new members are added in mmh_emu_t,
 * we need a specific cleanup routine to do some "clean" 
 * stuffs
 *
 * Parameters: 
 * None
 *
 * Return Values: 
 * None
 *
 * */
static void cleanup(void)
{
	/*
	while(--num_types!=0){
		free(emu_types[num_types].arg);
	}*/
	/*free(emu_types);*/
	free(mmh_rbuf);
	free(mmh_wbuf);
}

/*
 * Name: set_mode()
 *
 * Description: 
 * Set and record eumlation type
 *
 * Parameters: 
 * "type" arguments in cmd line
 *
 * Return Values: 
 * None
 *
 * */
static void set_mode(char *arg)
{
	/*struct mmh_emu_type *t;*/
	/*char *s;*/

	/*
	if((t=realloc(emu_types,sizeof(*t)*(num_types+1)))==NULL){
		err_quit("Out of Memory");
		cleanup();
		exit(1);
	}

	emu_types=t;
	*/

	if(strcasecmp(arg,"TCP_BULK")==0){
		/*emu_types[num_types].mode=TCP_BULK;*/
		mmh_emu_class.mode=TCP_BULK;
		mmh_udp_f=0;
		/*num_types++;*/
		/* left there for futhur use
		if((s=strdup(arg)==NULL)){
			err_quit("Out of memory");
			cleanup();
			exit(1)
		}
		emu_types[num_types-1].args=s;
		*/
	}else if(strcasecmp(arg,"TCP_MULTCONN")==0){
		/*emu_types[num_types].mode=TCP_MULTCONN;*/
		mmh_emu_class.mode=TCP_MULTCONN;
		mmh_udp_f=0;
		/*num_types++;*/
	}
}


static void usage(char *prog, char *msg)
{
	if(msg[0]!=0)
		err_msg("%s\n", msg);

	printf("Usage: \n");
	printf("  %s [ Options ] <Host> <Service/Port>		(For client; default)\n", prog);
	printf("  %s [ Options ] -s [ Host ] <Service/Port>	(For server)\n", prog);
	printf("Mammoth emulation client.\n");
	printf("Emulation TYPES:\n");
	printf("  TCP_BULK	TCP bulk data transfer, like FTP\n");
	printf("  TCP_MULTCONN	Multiple(Huge) TCP connections, like HTTP\n");
	/*printf("  -i,--install=MODULE_PKG   install a new module\n");*/
	/*printf*/
	    /*("  -l,--list-modules         display list of installed modules\n");*/
	printf("Other options:\n");
	printf("  -b,--bind		TCP or UDP port number to bind\n");
	printf("  -B,--broadcast	Set to use broadcast \n");
	printf("  -c,--connect		Number of multiple connections\n");
	printf("  -C,--conconnect	Use Concurrent server(Iterative default)\n");
	printf("  -f,--foreignaddr	foreign ip addr: a.b.c.d.port\n");
	printf("  -K,--keepalive	Set to enable SO_KEEPALIVE\n");
	printf("  -l,--localaddr	Local addr.port: a.b.c.d.p\n");
	printf("  -n,--numberwrite	Numbers of buffers to write\n");
	printf("  -p,--threadpoll	Numbers of threads in thread poll\n");
	printf("  -q,--listenqueue	Listen queue for TCP server\n");
	printf("  -r,--readsize		Bytes per read\n");
	printf("  -R,--rcvbuf		Socket read buffer\n");
	printf("  -s,--server		Acting as server, client in default\n");
	printf("  -S,--sndbuf		Socket write buffer\n");
	printf("  -T,--tcpmss		TCP max segment.i.e. TCP MSS\n");
	printf("  -u,--udpunconnect	Don't connect(UDP), connect default\n");
	printf("  -w,--writesize	Bytes per write\n");
	printf("  -h,--help		print this message and quit\n");
	printf("  -v,--verbose		be verbose\n");
	exit(1);
}


/*
 * Name: parse_cmd_line()
 *
 * Description: 
 * Parse the command line of mmhclient, set some flags 
 * and values according to the parameters user give.
 * Note: It's not thread-safe.
 *
 * Parameters: 
 * Same as the main() parameters
 *
 * Return Values: 
 * None
 *
 * */
static void parse_cmd_line(int argc, char **argv)
{
	static struct option opts[]={
		{"tcpmss",require_argument,NULL,'T'},
		{"broadcast",NULL,NULL,'B'},
		{"bind",require_argument,NULL,'b'},
		{"localaddr",require_argument,NULL,'l'},
		{"type",require_argument,NULL,'t'},
		{"connect",require_argument,NULL,'c'},
		{"concurrent",NULL,NULL,'C'},
		{"writesize",require_argument,NULL,'w'},
		{"readsize",require_argument,NULL,'r'},
		{"numberwrite",require_argument,NULL,'n'},
		{"rcvbuf",require_argument,NULL,'R'},
		{"sndbuf",require_argument,NULL,'S'},
		{"server",NULL,NULL,'s'},
		{"keepalive",NULL,NULL,'K'},
		{"foreignaddr",require_argument,NULL,'f'},
		{"listenqueue",require_argument,NULL,'q'},
		{"threadpoll",require_argument,NULL,'p'},
		{"udpunconnect",NULL,NULL,'u'},
		{"help",0,NULL,'h'},
		{"verbose",0,NULL,'v'},
		{NULL,0,NULL,0}
	};
	int rc;
	mmh_verbose=0;
	char *tmp=NULL;

	while((rc=getopt_long(argc,argv,"KBT:w:r:n:R:S:sb:l:t:c:Cf:p:uhv",opts,NULL))!=-1){
		switch(rc){
		case 'K':
			mmh_keepalive=1;
			break;
		case 'B':
			/* Set to be allowed to broadcast */
			mmh_broadcast=1;
			break;
		case 'T':
			mmh_tcpmss=atoi(optarg);
			break;
		case 'w':
			mmh_wsize=atoi(optarg);
			break;
		case 'r':
			mmh_rsize=atoi(optarg);
			break;
		case 'n':
			mmh_nbuf=atoi(optarg);
			break;
		case 'R':
			mmh_rcvbuf_size=atoi(optarg);
			break;
		case 'S':
			mmh_sndbuf_size=atoi(optarg);
			break;
		case 's':
			mmh_server=1;
			mmh_client=0;
			break;
		case 'b':
			mmh_bindport=atoi(optarg);
			break;
		case 'l':	/* local addr & port: a.b.c.d.p */
			if((tmp=strrchr(optarg,'.'))==NULL)
				usage("invalid -l options");
			*tmp++=0;
			bindport=atoi(tmp);
			strcpy(mmh_localaddr,optarg);
			break;
		case 't':
			set_mode(optarg);
			break;
		case 'c':
			mmh_connections=atoi(optarg);
			break;
		case 'C':
			mmh_concurrent=1;
			break;
		case 'f':	/* foreign ip addr and port: a.b.c.d.p */
			if((ptr=strrchr(optarg,'.'))==NULL)
				usage("invalid -f options");
			*ptr++=0;
			mmh_foreignport=atoi(ptr);
			strcpy(mmh_foreign,optarg);
			break;
		case 'S':
			mmh_listen_queue=atoi(optarg);
			break;
		case 'p':
			mmh_nthreads=atoi(optarg);
			break;
		case 'u':
			/* if user specifies -u, upd will not connect */
			mmh_udpconnect=0;
			break;
		case 'v':
			mmh_verbose=1;
			break;
		case 'h':
			usage(argv[0], NULL);
			exit(0);
		case '?':
		default:{
				usage(argv[0], NULL);
				exit(1);
			}
		}
	}

	/* check imcompatible options */
	if(mmh_udp_f==0 && mmh_broadcast)
		usage("can't specified -B with TCP");
	if(mmh_udp_f==0 && mmh_foreign[0]!=0)
		usage("can't specified -f with TCP");

	if(mmh_client){
		if(optind!=argc-2)
			usage(argv[0], "Missing <Host> and/or <Port>");
		mmh_host=argv[optind];
		mmh_service=argv[optind+1];
	}else{
		/*  
		 * server's "host" is wildcard or user-specified
		 *  */
		if(optind==argc-2){
			mmh_host=argv[optind];
			mmh_service=argv[optind+1];
		}else if(optind==argc-1){
			mmh_host=NULL;
			mmh_service=argv[optind];
		}else
			usage("missing <port>");
	}
}

int main(int argc, char **argv)
{
	int i;

	/* TCP_BULK is default, if user doesnt specify */
	set_mode("TCP_BULK");
	parse_cmd_line(argc,argv);
	
	/*
	for(i=0;i<num_types;i++){
		enum emu_modes mode=eum_types[i].mode;
		switch(mode){
		case 'TCP_BULK':{
					mmh_client_open(mmh_host,
							mmh_service);
					break;
				}
		case 'TCP_MULTCONN':{
					    break;
				    }
		}
	}
	*/

	if(mmh_server){
		/* All server stuffs are in mmh_server_run */
		mmh_server_run(mmh_host,mmh_service);
	}else if(mmh_client){
		/* "Connection" network stuffs */
		mmh_client_open(mmh_host,mmh_service);
		/* "Real" traffic genneration */
		switch(mmh_emu_class.mode){
		case 'TCP_BULK':{
				}
		case 'TCP_MULTCONN':{
				    }
		}
	}


cleanup:
	cleanup();
	exit(0);
}


